package pcsv

import (
	"reflect"
	"strconv"
	"strings"

	"gitee.com/wangyubin/gutils/parsers"
)

const tagName = "pcsv"

type pcsvTag struct {
	ReadIndex  int // field index for read
	WriteIndex int // field index for write
}

func getTagInfo(tag string) pcsvTag {
	tags := strings.Split(tag, ",")
	var pt pcsvTag
	if len(tags) == 1 {
		pt = pcsvTag{
			ReadIndex:  parsers.ParseIntWithDefault(tags[0], -1),
			WriteIndex: parsers.ParseIntWithDefault(tags[0], -1),
		}
	} else {
		pt = pcsvTag{
			ReadIndex:  parsers.ParseIntWithDefault(tags[0], -1),
			WriteIndex: parsers.ParseIntWithDefault(tags[1], -1),
		}
	}

	return pt
}

// Marshal struct to csv row([]string)
func Marshal(v interface{}) ([]string, error) {
	var ret = make([]string, 0)

	rv := reflect.ValueOf(v)
	val := rv.Elem()
	for i := 0; i < val.NumField(); i++ {
		tag := val.Type().Field(i).Tag.Get(tagName)

		if tag == "" || tag == "-" {
			continue
		}

		pt := getTagInfo(tag)
		if pt.WriteIndex == -1 {
			continue
		}

		if len(ret) < pt.WriteIndex+1 {
			for j := len(ret); j < pt.WriteIndex+1; j++ {
				ret = append(ret, "")
			}
		}

		switch val.Field(i).Type().Name() {
		case "int":
			ret[pt.WriteIndex] = strconv.FormatInt(val.Field(i).Int(), 10)
		case "int64":
			ret[pt.WriteIndex] = strconv.FormatInt(val.Field(i).Int(), 10)
		case "float":
			ret[pt.WriteIndex] = parsers.Float64String(val.Field(i).Float())
		case "float64":
			ret[pt.WriteIndex] = parsers.Float64String(val.Field(i).Float())
		default:
			ret[pt.WriteIndex] = val.Field(i).String()
		}
	}
	return ret, nil
}

// Unmarshal csv row([]string) to struct
func Unmarshal(data []string, v interface{}) error {
	rv := reflect.ValueOf(v)
	val := rv.Elem()
	for i := 0; i < val.NumField(); i++ {
		tag := val.Type().Field(i).Tag.Get(tagName)

		if tag == "" || tag == "-" {
			continue
		}

		pt := getTagInfo(tag)
		if pt.ReadIndex == -1 {
			continue
		}

		switch val.Field(i).Type().Name() {
		case "int":
			val.Field(i).SetInt(parsers.ParseInt64WithDefault(data[pt.ReadIndex], 0))
		case "int64":
			val.Field(i).SetInt(parsers.ParseInt64WithDefault(data[pt.ReadIndex], 0))
		case "float":
			val.Field(i).SetFloat(parsers.ParseFloat64WithDefault(data[pt.ReadIndex], 0.0))
		case "float64":
			val.Field(i).SetFloat(parsers.ParseFloat64WithDefault(data[pt.ReadIndex], 0.0))
		default:
			val.Field(i).SetString(data[pt.ReadIndex])
		}
	}
	return nil
}
